package service

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"fx_swift_boss/api"
	"fx_swift_boss/internal/model"
	"fx_swift_boss/pkg/code"
	"fx_swift_boss/pkg/global"
	"fx_swift_boss/pkg/gorsa"
	"fx_swift_boss/pkg/httputils"
	"fx_swift_boss/pkg/internalerrors"
	"fx_swift_boss/pkg/logger"
	"fx_swift_boss/pkg/utils"
	"github.com/gogf/gf/v2/container/gvar"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/shopspring/decimal"
	"github.com/spf13/cast"
	"io"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

func VerifyEmailFormat(email string) bool {
	pattern := `\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*` //匹配电子邮箱
	reg := regexp.MustCompile(pattern)
	return reg.MatchString(email)
}

// 代理商扣减，boss加入
func BalanceRecord(agentAccount, bossAccount, desc string, balance decimal.Decimal, tx gdb.TX) error {
	var (
		sqlRecord    sql.Result
		rowsAffected int64
		err          error
		accountObj   *model.FxAccount
	)
	if sqlRecord, err = tx.Model("fx_account").Data("balance = balance - ?", balance).
		Where("balance-? >= 0 and account=? and account_type='agent'", balance, agentAccount).Update(); err != nil {
		return internalerrors.New(code.ErrUnknown, err.Error())
	}

	rowsAffected, _ = sqlRecord.RowsAffected()
	if rowsAffected == 0 {
		logger.SugarLogger.Error(err.Error())
		return internalerrors.New(code.ErrUnknown, "数据库更新失败")
	}

	if err = tx.Model("fx_account").
		Where("account=?", agentAccount).Scan(&accountObj); err != nil {
		return internalerrors.New(code.ErrUnknown, "数据查询失败")
	}

	// 金额流水记录
	if _, err = tx.Model("fx_record").Insert(&model.FxRecord{
		DiscountOrderNo:  utils.GenerateCode64(),
		Account:          agentAccount,
		ActualAmount:     balance,
		Amount:           balance,
		BudgetSign:       0,
		AccountType:      "agent",
		RecordType:       model.Management,
		Desc:             desc + "扣除",
		RemainingBalance: accountObj.Balance.InexactFloat64(),
	}); err != nil {
		return internalerrors.New(code.ErrUnknown, "数据库更新失败")
	}

	// boss
	if sqlRecord, err = tx.Model("fx_account").Data("balance = balance + ?", balance).
		Where("account='admin' and account_type='boss'").Update(); err != nil {
		return internalerrors.New(code.ErrUnknown, err.Error())
	}

	rowsAffected, _ = sqlRecord.RowsAffected()
	if rowsAffected == 0 {
		logger.SugarLogger.Error(err.Error())
		return internalerrors.New(code.ErrUnknown, "数据库更新失败")
	}

	if _, err = tx.Model("fx_record").Insert(&model.FxRecord{
		DiscountOrderNo: utils.GenerateCode64(),
		Account:         bossAccount,
		ActualAmount:    balance,
		Amount:          balance,
		BudgetSign:      1,
		AccountType:     "boss",
		RecordType:      model.Management,
		Desc:            desc + "入账",
	}); err != nil {
		return internalerrors.New(code.ErrUnknown, err.Error())
	}
	return err
}

func UpdateAccountBalanceTx(obj *model.FxDiscountOrder, bossAccount, recordType string, tx gdb.TX) error {
	var (
		sqlRecord    sql.Result
		rowsAffected int64
		err          error
		accountObj   *model.FxAccount
	)

	// 代理商 金额变动
	if sqlRecord, err = tx.Model("fx_account").Data("balance = balance + ?", obj.ActualAmount).
		Where("account=? and account_type='agent'", obj.Account).Update(); err != nil {
		return internalerrors.New(code.ErrUnknown, err.Error())
	}
	rowsAffected, _ = sqlRecord.RowsAffected()
	if rowsAffected == 0 {
		logger.SugarLogger.Error(err.Error())
		return internalerrors.New(code.ErrUnknown, "数据库更新失败")
	}

	if err = tx.Model("fx_account").
		Where("account=?", obj.Account).Scan(&accountObj); err != nil {
		return internalerrors.New(code.ErrUnknown, "数据查询失败")
	}
	// 金额流水记录
	if _, err = tx.Model("fx_record").Insert(&model.FxRecord{
		DiscountOrderNo:  utils.GenerateCode64(),
		Account:          obj.Account,
		ActualAmount:     obj.ActualAmount,
		Amount:           obj.Amount,
		BudgetSign:       1,
		AccountType:      "agent",
		RecordType:       recordType,
		Desc:             "充值入账",
		RemainingBalance: accountObj.Balance.InexactFloat64(),
	}); err != nil {
		return internalerrors.New(code.ErrUnknown, err.Error())
	}

	// Boss
	if sqlRecord, err = tx.Model("fx_account").Data("balance = balance + ?", obj.Amount).
		Where("account='admin' and account_type='boss'").Update(); err != nil {
		return internalerrors.New(code.ErrUnknown, err.Error())
	}

	rowsAffected, _ = sqlRecord.RowsAffected()
	if rowsAffected == 0 {
		logger.SugarLogger.Error(err.Error())
		return internalerrors.New(code.ErrUnknown, "数据库更新失败")
	}

	if _, err = tx.Model("fx_record").Insert(&model.FxRecord{
		DiscountOrderNo: utils.GenerateCode64(),
		Account:         bossAccount,
		ActualAmount:    obj.ActualAmount,
		Amount:          obj.Amount,
		BudgetSign:      1,
		AccountType:     "boss",
		RecordType:      recordType,
		Desc:            "充值入账",
	}); err != nil {
		return internalerrors.New(code.ErrUnknown, err.Error())
	}

	if sqlRecord, err = tx.Model("fx_account").Data("balance = balance - ?", obj.Amount).
		Where("balance-? >= 0 and account_type='boss'", obj.ActualAmount).Update(); err != nil {
		return internalerrors.New(code.ErrUnknown, err.Error())
	}

	rowsAffected, _ = sqlRecord.RowsAffected()
	if rowsAffected == 0 {
		logger.SugarLogger.Error(err.Error())
		return internalerrors.New(code.ErrUnknown, "数据库更新失败")
	}

	if _, err = tx.Model("fx_record").Insert(&model.FxRecord{
		DiscountOrderNo: utils.GenerateCode64(),
		Account:         bossAccount,
		ActualAmount:    obj.ActualAmount,
		Amount:          obj.Amount,
		BudgetSign:      0,
		AccountType:     "boss",
		RecordType:      recordType,
		Desc:            "充值划扣",
	}); err != nil {
		return internalerrors.New(code.ErrUnknown, err.Error())
	}
	return nil
}

// 获取国家信息-结汇
func GetCountryInfo(ctx context.Context) ([]*api.GetCountryRes, error) {
	var (
		err         error
		countryList []*model.FxCountry
	)
	if err = global.GFDB.Schema("fx").Model("fx_country").Scan(&countryList); err != nil {
		logger.SugarLogger.Error(err)
		return nil, internalerrors.New(code.ErrUnknown, "数据查询异常")
	}
	result := make([]*api.GetCountryRes, 0)
	for _, value := range countryList {
		result = append(result, &api.GetCountryRes{
			Id: value.ID,
			NameInfo: api.NameInfo{
				ChineseName: value.ChineseName,
				EnglishName: value.EnglishName,
			},
			CountryCode:    value.CountryCode,
			IsoCountryCode: value.IsoCountryCode,
			Status:         value.Status,
		})
	}
	return result, err
}

// 获取国家信息-外汇
func GetForexCountryInfo(ctx context.Context) ([]*api.GetCountryRes, error) {
	var (
		err         error
		countryList []*model.ForexCountry
	)
	if err = global.GFDB.Schema("forex").Model("forex_country").OrderDesc("chinese_name").Scan(&countryList); err != nil {
		logger.SugarLogger.Error(err)
		return nil, internalerrors.New(code.ErrUnknown, "数据查询异常")
	}
	result := make([]*api.GetCountryRes, 0)
	for _, value := range countryList {
		result = append(result, &api.GetCountryRes{
			Id: value.ID,
			NameInfo: api.NameInfo{
				ChineseName: value.ChineseName,
				EnglishName: value.EnglishName,
			},
			CountryCode:    value.CountryCode,
			IsoCountryCode: value.IsoCountryCode,
			Status:         value.Status,
		})
	}
	return result, err
}

// 获取币种对应的编码和中文
func GetCurrencyInfo(ctx context.Context) ([]*api.GetCurrencyInfoRes, error) {
	var (
		err          error
		currencyList []*model.IfcgateCurrency
	)
	if err = global.GFDB.Schema("fx").Model("ifcgate_currency").Where("1=1").Scan(&currencyList); err != nil {
		logger.SugarLogger.Error(err)
		return nil, internalerrors.New(code.ErrUnknown, "数据查询异常")
	}
	result := make([]*api.GetCurrencyInfoRes, 0)
	for _, value := range currencyList {
		result = append(result, &api.GetCurrencyInfoRes{
			CurrencyCode: value.CurrencyCode,
			CurrencyName: value.CurrencyName,
			Image:        value.Image,
		})
	}
	return result, err
}

func GetPurposeList(ctx context.Context) ([]*api.PurposeRes, error) {
	var (
		err        error
		purposeObj []*model.IFCPurpose
	)

	if err = global.GFDB.Schema("fx").Model("ifcgate_purpose_base").Scan(&purposeObj); err != nil {
		logger.SugarLogger.Error(err)
		return []*api.PurposeRes{}, internalerrors.New(code.ErrUnknown, "数据查询异常")
	}
	if purposeObj == nil {
		logger.SugarLogger.Error(err)
		return []*api.PurposeRes{}, nil
	}

	purposeList := make([]*api.PurposeRes, 0)
	for _, value := range purposeObj {
		purposeList = append(purposeList, &api.PurposeRes{
			Code: value.Code,
			Desc: api.DescList{
				DescZh: value.DescZh,
				DescEn: value.DescEn,
			},
		})
	}

	return purposeList, nil
}

func FrontEndConfig() (string, error) {
	nacosMap := global.ViperObj.Get("bossconfig.list")
	jsonData, err := json.Marshal(nacosMap)
	if err != nil {
		return "", err
	}
	str := string(jsonData)
	return str, nil
}

func ConvertMerChantReq(req *api.MerChantVAReq) (*api.MerChantVAReq, error) {
	if req == nil {
		return nil, internalerrors.New(code.ErrUnknown, "请求参数为空")
	}
	converReq := req
	byteReq, _ := json.Marshal(converReq)
	reqStr := string(byteReq)

	var filePath string
	tarGetPath := global.ViperObj.GetString("sftp.targetPath")
	if converReq.CountryCode == "CN" {
		if len(converReq.EnterprisePath) == 0 {
			return nil, internalerrors.New(code.ErrUnknown, "全国工商企业文件不能为空")
		}
		filePath = converReq.EnterprisePath[0]
	} else {
		if len(converReq.CompanyCertificate) == 0 {
			return nil, internalerrors.New(code.ErrUnknown, "公司注册证书不能为空")
		}
		filePath = converReq.CompanyCertificate[0]
	}
	//将req中的上传路径替换成sftp的路径
	lastPath := strings.LastIndex(filePath, "/")
	subStr := filePath[:lastPath]
	reqStr = strings.ReplaceAll(reqStr, subStr, tarGetPath)

	var reqObj *api.MerChantVAReq
	err := json.Unmarshal([]byte(reqStr), &reqObj)
	if err != nil {
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	return reqObj, nil
}

// 请求OTTPay参数封装
func OperatorParamToPost(tradeCode, accessToken string, body []byte) ([]byte, error) {
	var (
		err  error
		resp string
		url  string
	)
	url = global.ViperObj.GetString("ottpay.baseUrl") + tradeCode
	//组装请求头参数
	reqMap := httputils.GetRequestParam(tradeCode, accessToken)
	//组装请求体参数
	reqBody := make(map[string]interface{})
	err = json.Unmarshal(body, &reqBody)
	if err != nil {
		return nil, err
	}
	reqMap["body"] = reqBody

	//转换成byte
	jsonData, err := json.Marshal(reqMap)
	fmt.Println(string(jsonData))
	//请求参数加密
	request := utils.Encrtpt(string(jsonData), global.ViperObj.GetString("ottpay.merChantNo"))
	reqJson, _ := json.Marshal(request)
	resp = httputils.DoPost(url, reqJson)
	if resp == "" {
		logger.SugarLogger.Errorf("响应报文为空:%s", resp)
		return nil, errors.New("响应报文为空")
	}
	var resBean = &api.Bean{}
	err = json.Unmarshal([]byte(resp), &resBean)
	if err != nil {
		logger.SugarLogger.Errorf("json 读取报错:%w", err)
		return nil, err
	}
	//解密并且封装实体
	resStr, err := utils.Decrypt(resBean)
	if err != nil {
		logger.SugarLogger.Errorf("响应报文解密报错:%w", err)
		return nil, err
	}
	headObj := &api.JsonStr{}
	err = json.Unmarshal(resStr, &headObj)
	//校验返回结果是否有异常，有则返回异常信息
	if headObj.Head.RespDesc != "请求成功" && headObj.Head.RespDesc != "S00000" {
		return nil, errors.New(headObj.Head.RespDesc)
	}
	return resStr, err
}

// 从redis读取comply平台的accessToken
func GetComplyTokenToRedis(ctx context.Context) (string, error) {
	var (
		err         error
		redisResult *gvar.Var
		accessToken string
	)

	redisResult, err = g.Redis().Get(ctx, global.ViperObj.GetString("comply.tokenFlag")+global.ViperObj.GetString("comply.userName"))
	if err != nil || redisResult.Val() == nil {
		accessToken, err = GetComplyToken(ctx)
		if err != nil {
			return "", internalerrors.New(code.ErrUnknown, err.Error())
		}
	} else {
		accessToken = redisResult.String()
	}
	return accessToken, err
}

func GetProvinceInfo(ctx context.Context) ([]*api.ProvinceRes, error) {
	var (
		err         error
		provinceObj []*api.ProvinceRes
	)
	if err = global.GFDB.Schema("forex").Model("forex_province_city_base").Fields("country_subdivision").Distinct().Scan(&provinceObj); err != nil {
		logger.SugarLogger.Error(err)
		return nil, internalerrors.New(code.ErrUnknown, "数据查询异常")
	}
	result := make([]*api.ProvinceRes, 0)
	for _, value := range provinceObj {
		result = append(result, &api.ProvinceRes{
			CountrySubdivision: value.CountrySubdivision,
		})
	}
	return result, err
}

func GetCityInfo(ctx context.Context, countrySubdivision string) ([]*api.CityRes, error) {
	var (
		err     error
		cityObj []*api.CityRes
	)

	if err = global.GFDB.Schema("forex").Model("forex_province_city_base").Fields("town_name").Where("country_subdivision=?", countrySubdivision).Scan(&cityObj); err != nil {
		logger.SugarLogger.Error(err)
		return nil, internalerrors.New(code.ErrUnknown, "数据查询异常")
	}
	result := make([]*api.CityRes, 0)
	for _, value := range cityObj {
		result = append(result, &api.CityRes{
			TownName: value.TownName,
		})
	}
	return result, err
}

// 获取公司类型
func GetCompanyType(ctx context.Context) ([]*api.CompanyTypeRes, error) {
	var (
		err            error
		companyTypeObj []*api.CompanyTypeRes
	)

	if err = global.GFDB.Schema("forex").Ctx(ctx).Model("forex_company_type_base").Scan(&companyTypeObj); err != nil {
		logger.SugarLogger.Error(err)
		return nil, internalerrors.New(code.ErrUnknown, "查询数据报错")
	}
	if companyTypeObj == nil {
		return []*api.CompanyTypeRes{}, nil
	}
	return companyTypeObj, err
}

// 获取公司类型
func GetBusinessType(ctx context.Context) ([]*api.BusinessTypeRes, error) {
	var (
		err             error
		businessTypeObj []*api.BusinessTypeRes
	)

	if err = global.GFDB.Schema("forex").Ctx(ctx).Model("forex_business_type_base").Scan(&businessTypeObj); err != nil {
		logger.SugarLogger.Error(err)
		return nil, internalerrors.New(code.ErrUnknown, "查询数据报错")
	}
	if businessTypeObj == nil {
		return []*api.BusinessTypeRes{}, nil
	}
	return businessTypeObj, err
}

func OperatorParams(url string, req interface{}) (string, error) {
	var (
		err       error
		resultStr string
		body      []byte
	)
	body, err = json.Marshal(&req)
	if err != nil {
		return "", internalerrors.New(code.ErrUnknown, err.Error())
	}
	resultStr, err = sendRequestToGep(url, string(body))
	if err != nil {
		return "", internalerrors.New(code.ErrUnknown, err.Error())
	}
	return resultStr, err
}

func sendRequestToGep(urls, params string) (string, error) {
	var (
		err     error
		body    []byte
		resp    *http.Response
		respObj *api.GenResponseVo
	)
	// 私钥加密
	prienctypt_str, err := gorsa.PriKeyEncrypt(params, gorsa.GetPrivateKeyStr())
	if err != nil {
		return "", err
	}
	// 将 formData 转换为 application/x-www-form-urlencoded 格式
	formData := url.Values{}
	formData.Set("version", "1.0.0")
	formData.Set("certificateId", global.ViperObj.GetString("gep.certificateId"))
	formData.Set("userNo", global.ViperObj.GetString("gep.userNo"))
	formData.Set("dataType", "JSON")
	formData.Set("dataContent", prienctypt_str)

	headers := map[string]string{
		"Content-Type": "application/x-www-form-urlencoded",
	}
	resp, err = httputils.HTTPPost(urls, headers, []byte(formData.Encode()))
	if resp.StatusCode != 200 || err != nil {
		return "", err
	}
	body, err = io.ReadAll(resp.Body)
	err = json.Unmarshal(body, &respObj)
	if err != nil {
		return "", err
	}
	if respObj.Success == true {
		return respObj.Result, err
	} else {
		return "", errors.New(respObj.ErrorMsg)
	}
}

func SendRequestToGepToJson(urls, params string) (*api.GetCardImageVo, error) {
	var (
		err       error
		body      []byte
		jsonBody  []byte
		resp      *http.Response
		respObj   *api.GenResponseVoToJson
		resultObj *api.GetCardImageVo
	)
	// 私钥加密
	prienctypt_str, err := gorsa.PriKeyEncrypt(params, gorsa.GetPrivateKeyStr())
	if err != nil {
		return nil, err
	}
	// 将 formData 转换为 application/x-www-form-urlencoded 格式
	formData := url.Values{}
	formData.Set("version", "1.0.0")
	formData.Set("certificateId", global.ViperObj.GetString("gep.certificateId"))
	formData.Set("userNo", global.ViperObj.GetString("gep.userNo"))
	formData.Set("dataType", "JSON")
	formData.Set("dataContent", prienctypt_str)

	headers := map[string]string{
		"Content-Type": "application/x-www-form-urlencoded",
	}
	resp, err = httputils.HTTPPost(urls, headers, []byte(formData.Encode()))
	if resp.StatusCode != 200 || err != nil {
		return nil, err
	}
	body, err = io.ReadAll(resp.Body)
	err = json.Unmarshal(body, &respObj)
	if err != nil {
		return nil, err
	}
	if respObj.Success == true {
		jsonBody, _ = json.Marshal(respObj.Result)
		json.Unmarshal(jsonBody, &resultObj)
		return resultObj, err
	} else {
		return nil, errors.New(respObj.ErrorMsg)
	}
}

func PostTp1002(req *api.Tp1002Req, merchantToken string) (*api.Tp1002Res, error) {
	var (
		err    error
		body   []byte
		resStr []byte
	)
	body, err = json.Marshal(&req)
	if err != nil {
		logger.SugarLogger.Errorf("json转换:%w", err)
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	resStr, err = OperatorParamToPost("tp1002", merchantToken, body)
	if err != nil {
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	ottPayResult := &api.Tp1002Res{}
	err = json.Unmarshal(resStr, &ottPayResult)
	if err != nil {
		logger.SugarLogger.Errorf("json转换:%w", err)
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	if ottPayResult.Head.RespDesc == "请求成功" && ottPayResult.Head.RespCode == "S00000" {
		return ottPayResult, err
	}
	return nil, err
}

func PostTp3004(req *api.Tp3004Req, merchantToken string) (*api.Tp3004Res, error) {
	var (
		err    error
		body   []byte
		resStr []byte
	)
	body, err = json.Marshal(&req)
	if err != nil {
		logger.SugarLogger.Errorf("json转换:%w", err)
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	resStr, err = OperatorParamToPost("tp3004", merchantToken, body)
	if err != nil {
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	ottPayResult := &api.Tp3004Res{}
	err = json.Unmarshal(resStr, &ottPayResult)
	if err != nil {
		logger.SugarLogger.Errorf("json转换:%w", err)
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	if ottPayResult.Head.RespDesc == "请求成功" && ottPayResult.Head.RespCode == "S00000" {
		return ottPayResult, err
	}
	return nil, err
}

// 将币种转为USD
func TransferCurrencyToUSD(currency, balance string) (float64, error) {
	var (
		err         error
		rateObj     *model.IfcgateRate
		finalAmount float64
	)
	if err = global.GFDB.Schema("fx").Model("ifcgate_rate").Fields("rate").
		Scan(&rateObj, "currency=?", currency); err != nil {
		logger.SugarLogger.Error(err)
		return finalAmount, err
	}
	fBalance := fmt.Sprintf("%.4f", cast.ToFloat64(balance)/rateObj.Rate)
	finalAmount = cast.ToFloat64(fBalance)
	return finalAmount, err
}

// 所有账户的币种转为USD
func AccountsCurrencyToUSD(merchantToken string) (map[string]float64, error) {
	var (
		err          error
		tp3004Result *api.Tp3004Res
	)
	isNoUsdMap := make(map[string]float64)
	isUsdMap := make(map[string]float64)
	finalMap := make(map[string]float64)
	tp3004Result, err = PostTp3004(&api.Tp3004Req{}, merchantToken)
	if len(tp3004Result.Tp3004InfoList.Tp3004Info) > 0 {
		for _, valueObj := range tp3004Result.Tp3004InfoList.Tp3004Info {
			if valueObj.Balance != "0.00" && valueObj.Currency != "USD" {
				if _, ok := isNoUsdMap[valueObj.Currency]; !ok {
					isNoUsdMap[valueObj.Currency] = cast.ToFloat64(valueObj.Balance)
				} else {
					tAmount := isNoUsdMap[valueObj.Currency]
					isNoUsdMap[valueObj.Currency] = cast.ToFloat64(valueObj.Balance) + tAmount
				}
			} else {
				if _, ok := isUsdMap[valueObj.Currency]; !ok {
					isUsdMap[valueObj.Currency] = cast.ToFloat64(valueObj.Balance)
				} else {
					tAmount := isUsdMap[valueObj.Currency]
					isUsdMap[valueObj.Currency] = cast.ToFloat64(valueObj.Balance) + tAmount
				}
			}
		}
		for key, value := range isNoUsdMap {
			finalMap[key] = value
		}
		for key, value := range isUsdMap {
			finalMap[key] = value
		}
	}
	return finalMap, err
}

// 通过merchantNo获取ottPayToken
func GetMerchantToToken(ctx context.Context, merchantNo string) (string, error) {
	var (
		err         error
		redisResult *gvar.Var
	)
	redisResult, err = g.Redis().Get(ctx, merchantNo)
	if err != nil || redisResult.Val() == nil {
		logger.SugarLogger.Error(err)
		return "", nil
	}
	return redisResult.String(), err
}
